/*****************************************************************************
 *   Copyright(C)2009-2019 by VSF Team                                       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/

/*============================ INCLUDES ======================================*/

#include "./uart.h"
#ifdef __WIN__
//for CreateFile...
#   include <Windows.h>
#else
//TODO: replace CreateFile...
#endif


/*============================ MACROS ========================================*/
/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/
/*============================ PROTOTYPES ====================================*/
/*============================ LOCAL VARIABLES ===============================*/
/*============================ GLOBAL VARIABLES ==============================*/
/*============================ IMPLEMENTATION ================================*/

static void __vk_usart_init_evt_thread(void *arg)
{
    vsf_arch_irq_request_t *irq_thread = arg;
    __vsf_arch_irq_set_background(irq_thread);

    vsf_usart_t *usart_t = container_of(irq_thread, vsf_usart_t, irq_thread);
    HANDLE handle_com;
    int res = 0;

    char file[6] = { 0 };
    /* Cannot be generated by macro link */
    switch (usart_t->com_num){
    case 1:handle_com = CreateFile(TEXT("com1"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 2:handle_com = CreateFile(TEXT("com2"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 3:handle_com = CreateFile(TEXT("com3"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 4:handle_com = CreateFile(TEXT("com4"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 5:handle_com = CreateFile(TEXT("com5"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 6:handle_com = CreateFile(TEXT("com6"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 7:handle_com = CreateFile(TEXT("com7"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 8:handle_com = CreateFile(TEXT("com8"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    case 9:handle_com = CreateFile(TEXT("com9"), GENERIC_WRITE | GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
        break;
    default:
        VSF_HAL_ASSERT(NULL);
    }
    res = SetupComm(handle_com, 1024, 1024);
    //VSF_HAL_ASSERT(res);
    usart_t->handle_com = handle_com;

    COMMTIMEOUTS timeOuts;
    timeOuts.ReadIntervalTimeout = usart_t->cfg->rcv_timeout;
    timeOuts.ReadTotalTimeoutConstant = 0;
    timeOuts.ReadTotalTimeoutMultiplier = 0;
    res = SetCommTimeouts(handle_com, &timeOuts);
    //VSF_HAL_ASSERT(res);

    DCB dcb;
    res = GetCommState(handle_com, &dcb);
    //VSF_HAL_ASSERT(res);
    dcb.fAbortOnError = FALSE;
    dcb.BaudRate = usart_t->cfg->baudrate;
    dcb.ByteSize = USART_9_BIT_LENGTH & usart_t->cfg->mode ? 9 : 8;
    dcb.Parity = USART_ODD_PARITY & usart_t->cfg->mode ? 1 :
        (USART_EVEN_PARITY & usart_t->cfg->mode ? 2 : 0);
    dcb.StopBits = USART_2_STOPBIT & usart_t->cfg->mode ? 2 : 0;
    dcb.fRtsControl = FALSE;
    dcb.fOutxCtsFlow = FALSE;
    if ((USART_RTS_CTS_HWCONTROL & usart_t->cfg->mode) == USART_RTS_CTS_HWCONTROL){
        dcb.fRtsControl = TRUE;
        dcb.fOutxCtsFlow = TRUE;
    } else if ((USART_CTS_HWCONTROL & usart_t->cfg->mode) == USART_CTS_HWCONTROL){
        dcb.fOutxCtsFlow = TRUE;
    } else if ((USART_RTS_HWCONTROL & usart_t->cfg->mode) == USART_RTS_HWCONTROL){
        dcb.fRtsControl = TRUE;
    }
    res = SetCommState(handle_com, &dcb);
    //VSF_HAL_ASSERT(res);
    PurgeComm(handle_com, PURGE_TXCLEAR | PURGE_RXCLEAR);
    ClearCommError(handle_com, NULL, NULL);


    
    __vsf_arch_irq_start(irq_thread);
    vsf_usart_evt_enable(&usart_t, 0xff);
    //vsf_trace(VSF_TRACE_INFO, "__vk_usart_init_evt_thread\n");


    __vsf_arch_irq_end(irq_thread, false);
    __vsf_arch_irq_fini(irq_thread);
}
static void __vk_usart_read_byte_evt_thread(void *arg)
{
    vsf_arch_irq_request_t *irq_thread = arg;
    vsf_usart_t *usart_t = container_of(irq_thread, vsf_usart_t, irq_thread);

    __vsf_arch_irq_set_background(irq_thread);
   

        bool read_ret;
        uint8_t read_len;
        OVERLAPPED overLapped;

        memset(&overLapped, 0, sizeof(OVERLAPPED));
        overLapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        read_ret = ReadFile(usart_t->handle_com, usart_t->buf, 1, &read_len, &overLapped);

        if (!read_ret){
            if (GetLastError() == ERROR_IO_PENDING){
                GetOverlappedResult(usart_t->handle_com, &overLapped, &read_len, TRUE);
            }
        }
        if (read_len > 1){
            VSF_HAL_ASSERT(NULL);
        }
    __vsf_arch_irq_start(irq_thread);

    //vsf_trace(VSF_TRACE_INFO, "__vk_usart_read_byte_evt_thread\n");

    __vsf_arch_irq_end(irq_thread, false);
    __vsf_arch_irq_fini(irq_thread);

}
static void __vk_usart_read_block_evt_thread(void *arg)
{
    vsf_arch_irq_request_t *irq_thread = arg;
    vsf_usart_t *usart_t = container_of(irq_thread, vsf_usart_t, irq_thread);

    __vsf_arch_irq_set_background(irq_thread);
   

        bool read_ret;
        uint_fast32_t read_len;
        OVERLAPPED overLapped;

        memset(&overLapped, 0, sizeof(OVERLAPPED));
        overLapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        read_ret = ReadFile(usart_t->handle_com, usart_t->buf, usart_t->buf_size, &read_len, &overLapped);

        if (!read_ret){
            if (GetLastError() == ERROR_IO_PENDING){
                GetOverlappedResult(usart_t->handle_com, &overLapped, &read_len, TRUE);
            }
        }
        if (read_len != usart_t->buf_size){
            VSF_HAL_ASSERT(NULL);
        }
    __vsf_arch_irq_start(irq_thread);

    //vsf_trace(VSF_TRACE_INFO, "__vk_usart_read_block_evt_thread\n");

    __vsf_arch_irq_end(irq_thread, false);
    __vsf_arch_irq_fini(irq_thread);
}
static void __vk_usart_write_byte_evt_thread(void *arg)
{

    vsf_arch_irq_request_t *irq_thread = arg;
    vsf_usart_t *usart_t = container_of(irq_thread, vsf_usart_t, irq_thread);

    __vsf_arch_irq_set_background(irq_thread);
    

        bool write_ret;
        uint8_t write_len;
        OVERLAPPED overLapped;

        memset(&overLapped, 0, sizeof(OVERLAPPED));
        overLapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        write_ret = WriteFile(usart_t->handle_com, usart_t->buf, 1, &write_len, &overLapped);

        if (!write_ret){
            if (GetLastError() == ERROR_IO_PENDING){
                GetOverlappedResult(usart_t->handle_com, &overLapped, &write_len, TRUE);
            }
        }
        if (write_len > 1){
            VSF_HAL_ASSERT(NULL);
        }

    __vsf_arch_irq_start(irq_thread);
        //vsf_trace(VSF_TRACE_INFO, "__vk_usart_read_block_evt_thread\n");
    __vsf_arch_irq_end(irq_thread, false);
    __vsf_arch_irq_fini(irq_thread);
}
static void __vk_usart_write_block_evt_thread(void *arg)
{

    vsf_arch_irq_request_t *irq_thread = arg;
    vsf_usart_t *usart_t = container_of(irq_thread, vsf_usart_t, irq_thread);

    __vsf_arch_irq_set_background(irq_thread);
    

        bool write_ret;
        uint_fast32_t write_len;
        OVERLAPPED overLapped;

        memset(&overLapped, 0, sizeof(OVERLAPPED));
        overLapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        write_ret = WriteFile(usart_t->handle_com, usart_t->buf, usart_t->buf_size, &write_len, &overLapped);

        if (!write_ret){
            if (GetLastError() == ERROR_IO_PENDING){
                GetOverlappedResult(usart_t->handle_com, &overLapped, &write_len, TRUE);
            }
        }
        if (write_len != usart_t->buf_size){
            VSF_HAL_ASSERT(NULL);
        }
    __vsf_arch_irq_start(irq_thread);
    __vsf_arch_irq_end(irq_thread, false);
    __vsf_arch_irq_fini(irq_thread);
}

vsf_err_t vsf_usart_init(vsf_usart_t *usart, usart_cfg_t *cfg)
{
    //VSF_HAL_ASSERT(cfg != NULL);
    if (0 == cfg->baudrate)
        cfg->baudrate = 115200;
    //vsf_trace(VSF_TRACE_INFO, "cfg->baudrate = 115200;\n");
    usart->cfg = cfg;
    usart->com_status = USART_ENABLE;

    __vsf_arch_irq_request_init(&usart->irq_request);
    __vsf_arch_irq_init(&usart->irq_thread, "vsf_usart_event", __vk_usart_init_evt_thread, vsf_arch_prio_0);
    //vsf_trace(VSF_TRACE_INFO, "__vsf_arch_irq_init\n");
    //__vk_usart_init_evt_thread(&usart->irq_thread);
    return VSF_ERR_NONE;
}

fsm_rt_t vsf_usart_enable(vsf_usart_t *usart)
{
    usart->com_status |= USART_ENABLE;
    return fsm_rt_cpl;
}

fsm_rt_t vsf_usart_disable(vsf_usart_t *usart)
{
    usart->com_status &= USART_DISABLE;
    return fsm_rt_cpl;
}

usart_status_t vsf_usart_status(vsf_usart_t *usart)
{
    usart_status_t ret;
    ret.is_busy = (USART_IS_BUSY & usart->com_status) >> 1;
    ret.evt_status = usart->com_status & (USART_EVT_RD | USART_EVT_WD);
    return ret;
}

bool vsf_usart_read_byte(vsf_usart_t *usart, uint8_t *pbyte)
{
    if ((usart->able_flag & VSF_USART_EVT_RX) != 1){
        /* read byte evt disable */
        return FALSE;
    }
    if ((usart->com_status & USART_DISABLE) == USART_DISABLE){
        /* read byte com disable */
        return FALSE;
    }
    usart->buf = pbyte;
    if ((usart->user_fn_able & VSF_USART_EVT_RX) == VSF_USART_EVT_RX){
        usart->user_fn_rd_onebyt->handler_fn(usart->user_fn_rd_onebyt->target_ptr, usart, vsf_usart_status(usart));
    }
    __vsf_arch_irq_request_init(&usart->irq_request);
    __vsf_arch_irq_init(&usart->irq_thread, "vsf_usart_event", __vk_usart_read_byte_evt_thread, vsf_arch_prio_0);
    return TRUE;
}

bool vsf_usart_write_byte(vsf_usart_t *usart, uint_fast8_t byte)
{
    if ((usart->able_flag & VSF_USART_EVT_TX) != 1){
        return FALSE;
    }
    if ((usart->com_status & USART_DISABLE) == USART_DISABLE){
        return FALSE;
    }
    usart->buf = &byte;
    if ((usart->user_fn_able & VSF_USART_EVT_TX) == VSF_USART_EVT_TX){
        usart->user_fn_wd_onebyt->handler_fn(usart->user_fn_wd_onebyt->target_ptr, usart, vsf_usart_status(usart));
    }

    __vsf_arch_irq_request_init(&usart->irq_request);
    __vsf_arch_irq_init(&usart->irq_thread, "vsf_usart_event", __vk_usart_write_byte_evt_thread, vsf_arch_prio_0);
    return TRUE;
}

fsm_rt_t vsf_usart_request_read(vsf_usart_t *usart, uint8_t *buffer, uint_fast32_t size)
{
    if ((usart->able_flag & VSF_USART_EVT_RCV_BLK_CPL) != 1){
        return FALSE;
    }
    if ((usart->com_status & USART_DISABLE) == USART_DISABLE){
        return FALSE;
    }
    usart->buf = buffer;
    usart->buf_size = size;
    __vsf_arch_irq_request_init(&usart->irq_request);
    __vsf_arch_irq_init(&usart->irq_thread, "vsf_usart_event", __vk_usart_read_block_evt_thread, vsf_arch_prio_0);
    return TRUE;
}

fsm_rt_t vsf_usart_request_write(vsf_usart_t *usart, uint8_t *buffer, uint_fast32_t size)
{
    vsf_trace(VSF_TRACE_INFO, "vsf_usart_request_write in\n");
    if ((usart->able_flag & VSF_USART_EVT_SND_BLK_CPL) != 1){
        return FALSE;
    }
    if ((usart->com_status & USART_DISABLE) == USART_DISABLE){
        return fsm_rt_err;
    }
    usart->buf = buffer;
    usart->buf_size = size;
   
    __vsf_arch_irq_request_init(&usart->irq_request);
    __vsf_arch_irq_init(&usart->irq_thread, "vsf_usart_event", __vk_usart_write_block_evt_thread, vsf_arch_prio_0);
    vsf_trace(VSF_TRACE_INFO, "vsf_usart_request_write ok\n");
    //__vk_usart_write_block_evt_thread(&usart->irq_thread);
    return fsm_rt_cpl;
}

void vsf_usart_evt_register(vsf_usart_t *usart, vsf_usart_evt_type_t type, vsf_usart_evt_t evt)
{
    if ((VSF_USART_EVT_RX & type) == VSF_USART_EVT_RX){
        usart->user_fn_able = VSF_USART_EVT_RX;
        usart->user_fn_rd_onebyt = &evt;
    }
    if ((VSF_USART_EVT_TX & type) == VSF_USART_EVT_TX){
        usart->user_fn_able = VSF_USART_EVT_TX;
        usart->user_fn_wd_onebyt = &evt;
    }
    if ((VSF_USART_EVT_RCV_BLK_CPL & type) == VSF_USART_EVT_RCV_BLK_CPL){
        usart->user_fn_able = VSF_USART_EVT_RCV_BLK_CPL;
        usart->user_fn_rd_block = &evt;
    }
    if ((VSF_USART_EVT_SND_BLK_CPL & type) == VSF_USART_EVT_SND_BLK_CPL){
        usart->user_fn_able = VSF_USART_EVT_SND_BLK_CPL;
        usart->user_fn_wd_block = &evt;
    }
}

usart_evt_status_t vsf_usart_evt_enable(vsf_usart_t *usart, usart_evt_status_t evt_mask)
{
    if (usart->able_flag == 0xff)
        return usart->able_flag;
    return usart->able_flag |= evt_mask;
}

usart_evt_status_t vsf_usart_evt_disable(vsf_usart_t *usart, usart_evt_status_t evt_mask)
{
    if (usart->able_flag == 0x00)
        return usart->able_flag;
    return usart->able_flag &= evt_mask;
}

void vsf_usart_evt_resume(vsf_usart_t *usart, usart_evt_status_t evt_status)
{
    usart->able_flag = evt_status;
}
